MWeb 自动发布到 Hexo

需求

之前一直在寻找一个合适的方式来记录工作的日常,例如问题记录、方案计划等等。但一直没有找到一直适合自己的工作流程。主要还是卡在记录这一步骤。

记录 -> 记什么、记在哪里、怎么记。

以下将有几百字的废话,赶时间的可以直接下一章节

本项目已经上传到Github,需要的自行下载


为了解决记录问题,我尝试过Eventnote便签手写笔记本等等。最后发现都因为各种各样的原因让我放弃了。

Eventnote的编辑器、便签的同步问题、手机笔记本的搜索问题都在阻碍着我记录,知道最近遇到了hexo,才发现原来记录是如此的简单美好。

个人认为一个良好的工作流程应该如下:

  1. 接收到需求
  2. 计划
  3. 实施
  4. 记录
  5. 归档

计划和记录都可以选择自己喜欢的MarkDown工具,而我自然选择了MWeb,包含了所有该有的功能,查询,文件库管理以及图床功能。

MWeb进行日常的编写查询归档等功能,写好了之后用Alfred把指定的文章post到Hexo,其实也就是执行个脚本,把选中的MD处理一下,变成Hexo格式,然后就可以Post了,同时文件库用坚果云来进行同步。当然,你也可以选择其他的同步软件,例如iCould`Google Drive`等等。

可能有人疑惑,为什么不直接把MWeb的文件库改到Hexo的Post里面去,这样就可以直接编辑Hexo new出来的文件了啊。

正常新建的MWeb文件如图所示:
-w316
包含了标题,还有内容
但直接打开Hexo的MD,就会看不到Title,
效果如图所示:
-w295
虽然我不是处女座,但也忍受不了整个文件库里面都是这种—的标题显示,如果能接受的,你就可以直接用这种方式来处理了。

所以需求总结起来有以下几点。

  • 新建文章
  • 查询文章
  • 增加Tag功能
  • 自动发布到某篇文章到Hexo

新建文章

本来计划通过Alfred进行新建文章功能,查询了一下MWeb数据库
-w690
由于不知道文章ID的生成规则(只知道应该是用时间戳来标识文章ID的,但位数不对,多了4位),所以不敢轻易的往里面插入数据。
如果以后有这样的需求,再增加这个功能进去吧。

查询文章

具体实现参考了Github-MWeb-Alfred
主要也是通过查询数据库来实现的。具体可看原作者的Github-MWeb-Alfred

增加Tag功能

通过查看MWeb数据库
-w1016
可以得知,要得到一个文章的所有tag,则需要连表查询,具体的sqlite 语句如下

1
SELECT b.name FROM tag_article a,tag b WHERE ( a.aid='${fileid}' AND a.rid = b.id);

完整bash代码:searchtag.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#!/bin/bash
# DESCRIPTION: 将查询出来的md,进行二次查询,把tag汇总,输出tag1,tag2,...

# 环境变量和目录检查
if [ -z "${MDOC_HOME}" ];then
echo "{ \"items\":["
echo "{"
echo "\"type\": \"error\","
echo "\"title\": \"请设置环境变量MDOC_HOME\""
echo "}"
echo "]}"
exit 1
fi
MDOC_HOME=$(eval "echo ${MDOC_HOME}")
if [ ! -d "${MDOC_HOME}/docs" ];then
echo "{ \"items\":["
echo "{"
echo "\"type\": \"error\","
echo "\"title\": \"\\\"${MDOC_HOME}/docs\\\" 目录不存在\""
echo "}"
echo "]}"
exit 1
fi
cd "${MDOC_HOME}/docs"

params="${1}" # md文章id
IFS=';' read -r -a array <<< "${1}"
fileid="${array[0]}"
fileid="${fileid/.md/}"
title="${array[1]}"
taglist=""

# 输出文档列表的函数
output_result(){
if [ "${files}" = "" ];then
echo "${MDOC_HOME}/docs/${fileid}.md;${title};"
exit
fi
for i in ${files}
do
if [ "${taglist}" = "" ];then
taglist="${i}"
else
taglist="${taglist}","${i}"
fi
done
echo "${MDOC_HOME}/docs/${fileid}.md;${title};${taglist}"
exit
}

sql="SELECT b.name FROM tag_article a,tag b WHERE ( a.aid='${fileid}' AND a.rid = b.id);"
final_expr="sqlite3 \"${MDOC_HOME}/mainlib.db\" \"${sql}\""
final_expr="${final_expr}"
# echo "$final_expr"
files=`eval "${final_expr}"`
output_result

自动发布某篇文章到Hexo

搜索文章->查询文章所有的tag->增加Hexo能识别的内容->发布
我在原作者的查询里面修改了一下,因为查询出来的结果是/Users/rinfon/Desktop/work/workstyle/blogfile/docs/15471063755188.md这种路径的,而我需要一个文章的id和文章的title即可15471063755188;title,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#!/bin/bash

# NOTE: 本脚本要求所有参数用'或"合成一个参数,如 -t TODO 要输入为 '-t TODO' 或 "-t TODO"
# alfred 会将所有输入作为一个参数,包括末尾的空格

# 环境变量和目录检查
if [ -z "${MDOC_HOME}" ];then
echo "{ \"items\":["
echo "{"
echo "\"type\": \"error\","
echo "\"title\": \"请设置环境变量MDOC_HOME\""
echo "}"
echo "]}"
exit 1
fi
MDOC_HOME=$(eval "echo ${MDOC_HOME}")
if [ ! -d "${MDOC_HOME}/docs" ];then
echo "{ \"items\":["
echo "{"
echo "\"type\": \"error\","
echo "\"title\": \"\\\"${MDOC_HOME}/docs\\\" 目录不存在\""
echo "}"
echo "]}"
exit 1
fi
cd "${MDOC_HOME}/docs"

declare -a tag_arr # 标签数组
declare -a header_arr # 标题数组
declare -a category_arr # 分类数组
declare -a keyword_arr # 关键字数组
next_input=0 # 下一步输入参数归类,0:keyword, 1: tag, 2: header, 3: category
last_input=0 # 最后一次输入的参数归类;与next_input 相同
end_option=1 # 是否终止了除 keyword 以外类型的参数输入: 单最后一个字符为空格表示输出完成了
end_char="" # 最后一个参数最后输入的字符
# 收集参数的函数:
# 1.收集tag,header,keyword参数,分别放到 tag_arr,header_arr,keyword_arr中
# 2.判断用户一下个要输入的参数(next_input)
get_params(){
while [ $# -gt 0 ]; do
case "$1" in
-t)
# echo "$1"
next_input=1
last_input=${next_input}
shift;;
-h)
# echo "$1"
next_input=2
last_input=${next_input}
shift;;
# -c|--category)
# echo "$1"
# next_input=3
# last_input=${next_input}
# shift;;
*)
# echo "$1"
str=${1//,/,} # 中文逗号改成英文逗号
n=${#str}
end_char=${str:$((n-1))} # 记录最后一个字符
# 按照 next_input 指示,将参数放到对应的数组中。
case "${next_input}" in
1) IFS=","; for i in ${str};do tag_arr+=("$i"); done;unset IFS ;;
2) IFS=","; for i in ${str};do header_arr+=("$i"); done;unset IFS ;;
3) IFS=","; for i in ${str};do category_arr+=("$i"); done;unset IFS ;;
*) keyword_arr+=("$str") ;;
esac
last_input=${next_input} # 保存最后一次输入的参数类型
next_input=0;
shift;;
esac
done
}

get_params $* # 调用收集参数的函数
#echo tag_arr=${tag_arr[@]}
#echo header_arr=${header_arr[@]}
#echo category_arr=${category_arr[@]}
#echo keyword_arr=${keyword_arr[@]}
#echo next_input=${next_input}
#echo last_input=${last_input}

# 输出tag列表的函数
output_tags(){
if [ "${filtered_tags}" = "" ];then
echo "{\"items\":["
echo "{"
echo "\"title\": \"没有相关tag\","
echo "}"
echo "]}"
exit
fi
local separator=""
echo "{\"items\":["
for i in ${filtered_tags}
do
printf '%s' ${separator}
separator=","
echo "{"
echo "\"title\": \"tag: ${i}\","
echo "\"autocomplete\": \"${1}${i},\","
echo "\"valid\":\"no\""
echo "}"
done
echo "]}"
exit
}

# 输出文档列表的函数
output_files(){
if [ "${files}" = "" ];then
echo "{\"items\":["
echo "{"
echo "\"title\": \"没有找到符合条件的文档\","
echo "}"
echo "]}"
exit
fi
local separator=""
echo "{\"items\":["
for i in ${files}
do
printf '%s' ${separator}
separator=","
h="$(head -1 "${i}"| sed 's/\\/\\\\/g;s/"/\\"/g;s/[[:space:]]*$//g')"
echo "{"
echo "\"type\": \"file\","
echo "\"title\": \"${h}\","
echo "\"arg\": \"$i;$h\""
echo "}"
done
echo "]}"
}

# 最后一个字符不是空格,且最后一次输入归类不是0,这表示该类型参数没有输入完成 --> 可以输出该类型选项
if [ "$*" = "${*% }" -a ${last_input} -gt 0 ];then
end_option=0
# 下一个参数类型=最后一次输入类型,且类型不是0,表示该类型参数没有输入完成 --> 可以输出该类型选项
elif [ ${next_input} -gt 0 -a ${last_input} = ${next_input} ];then
end_option=0
else
end_option=1
fi
#echo "end_option=${end_option}"

## 如果当前输入为 tag,且没有结束输入,输出tag列表: 排除已经输入的tag,以当前输入为前缀的tag
if [ ${last_input} -eq 1 -a ${end_option} -eq 0 ]; then
autocomplete=''
n=${#tag_arr[@]}
if [ ${n} -eq 0 ];then # 还没有输入任何值
autocomplete="$1 "
sql="select name from tag";
elif [ "${end_char}" = "," ];then # 已输入若干个值,且最后一个值已确定
autocomplete="$1"
sql="select name from tag where 1=1";
for i in ${tag_arr[@]}
do
sql="${sql} and name not like '${i}'"
done
else # 已输入若干个值,且最后一个值还没有输入完成
n=$((n-1))
autocomplete="${1%${tag_arr[${n}]}}"
sql="select name from tag where name like '${tag_arr[@]:$n}%'";
for i in ${tag_arr[@]:0:${n}}
do
sql="${sql} and name not like '${i}'"
done
fi
# echo ${sql}
final_expr="sqlite3 \"${MDOC_HOME}/mainlib.db\" \"${sql}\""
filtered_tags=`eval "${final_expr}"`
output_tags "${autocomplete}"
exit
fi

## 如果有输入-t参数,过滤文档tag
## 查询文档SQL:
##SELECT a.aid FROM tag_article a,article b
##WHERE a.aid = b.uuid AND
## a.rid IN (SELECT id from tag b WHERE b.name LIKE 'TODO' or b.name LIKE 'DONE' )
##GROUP BY a.aid HAVING count(1) >=2
##ORDER BY b.dateModif DESC;
if [ ${#tag_arr[@]} -gt 0 ];then
sql='select id from tag where '
or_str=""
for i in ${tag_arr[@]}
do
sql="${sql} ${or_str} name like '${i}'"
or_str=or
done
sql="SELECT a.aid||'.md' FROM tag_article a,article b \
WHERE a.aid=b.uuid AND a.rid IN (${sql}) \
GROUP BY a.aid HAVING count(1)>=${#tag_arr[@]} \
ORDER BY b.dateModif desc";
# echo ${sql}
final_expr="sqlite3 \"${MDOC_HOME}/mainlib.db\" \"${sql}\""
else
final_expr="ls -t *.md"
fi

# 如果有输入-h参数,过滤文档标题
# 思路: grep -n 会输出行号,找到符合所有关键字的行,筛选行号=1的文档就可以了
if [ ${#header_arr[@]} -gt 0 ];then
final_expr="${final_expr} | xargs grep -inHe '${header_arr[0]}'"
for i in ${header_arr[@]:1}
do
final_expr="${final_expr} | grep -ie '${i}'"
done
final_expr="${final_expr} | egrep '^.+\.md\:1\:' | awk -F':' '{print \$1}'"
fi

# 如果有输入关键字,则用关键字筛选文档,并且按照文档标题匹配度排序
if [ ${#keyword_arr[@]} -gt 0 ];then
for i in ${keyword_arr[@]}
do
final_expr="${final_expr}| xargs grep -ile '${i}' | awk -F':' '{print \$1}' | uniq "
done

# 排序表达式: 统计第一行匹配关键字个数,将匹配个数大的放在前面
# 第一步: 输入"文件名",输出"文件名 匹配个数"
# 第二步: 按照 "匹配个数" 倒序排序
# 第三步: 去掉 "匹配个数" 字段,只保留"文件名"
# 由于ls -lt 是按照编辑时间倒序排序的,所以最终排序等级:标题匹配个数倒序->最后编辑倒序
egrep_expr="$(echo "${keyword_arr[@]}" | sed "s/[[:blank:]]/|/g")"
sort_expr="awk '{system(\"egrep -ioe \\\"${egrep_expr}\\\" <<< \`head -1 \"\$1 \"\`|wc -l | xargs echo \"\$1)}' | sort -rk 2 | awk '{print \$1}'"
final_expr="${final_expr} | ${sort_expr} "
fi

final_expr="${final_expr} | head -20 " # 限制最多输出20条记录
# echo "$final_expr"
files=`eval "${final_expr}"`
output_files

为了让Hexo识别,增加了一下脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
if [ ! -n "${HEXO_POST}" ] ;then
exit 1
fi

IFS=';' read -r -a array <<< "{query}"

path=${array[0]}
title=${array[1]}
title="${title/\# /}"
taglist=${array[2]}

cp "$path" "${HEXO_POST}/${title}.md"

cd "${HEXO_POST}"
#删除title
sed -i '' 1d ./"${title}.md"
sed -i '' '1i\'$'\n---\n' ./"${title}.md"
sed -i '' "1 a\
title: $title
" ./"${title}.md"

sed -i '' "2 a\
tag: [${taglist}]
" ./"${title}.md"
sed -i '' '3 a\'$'\n---\n' ./"${title}.md"



linecount=$(cat ./"${title}.md" | wc -l )

if [ $linecount -gt 11 ];then
sed -i '' "12 a\
<!--more-->
" ./"${title}.md"
fi

PATH=${HEXO_CMD_PATH}
PATH=${PATH}:${GIT_PATH}
hexo clean
hexo g
hexo d

遇到的问题

  • alfred 执行git、hexo命令找不到问题
    需要把git的命令路径和hexo路径填写在alfred的环境变量里面。不然执行会git和hexo的操作会失败。
-------------本文结束感谢您的阅读-------------
您的支持将是我最大的动力